/*
 * Behave.js
 *
 * Copyright 2013, Jacob Kelley - http://jakiestfu.com/
 * Released under the MIT Licence
 * http://opensource.org/licenses/MIT
 *
 * Github:  http://github.com/jakiestfu/Behave.js/
 * Version: 1.5
 */


(function(undefined){

    'use strict';

    var BehaveHooks = BehaveHooks || (function(){
                var hooks = {};

                return {
                    add: function(hookName, fn){
                        if(typeof hookName == "object"){
                            var i;
                            for(i=0; i<hookName.length; i++){
                                var theHook = hookName[i];
                                if(!hooks[theHook]){
                                    hooks[theHook] = [];
                                }
                                hooks[theHook].push(fn);
                            }
                        } else {
                            if(!hooks[hookName]){
                                hooks[hookName] = [];
                            }
                            hooks[hookName].push(fn);
                        }
                    },
                    get: function(hookName){
                        if(hooks[hookName]){
                            return hooks[hookName];
                        }
                    }
                };

            })(),
        Behave = Behave || function (userOpts) {

                if (typeof String.prototype.repeat !== 'function') {
                    String.prototype.repeat = function(times) {
                        if(times < 1){
                            return '';
                        }
                        if(times % 2){
                            return this.repeat(times - 1) + this;
                        }
                        var half = this.repeat(times / 2);
                        return half + half;
                    };
                }

                if (typeof Array.prototype.filter !== 'function') {
                    Array.prototype.filter = function(func /*, thisp */) {
                        if (this === null) {
                            throw new TypeError();
                        }

                        var t = Object(this),
                            len = t.length >>> 0;
                        if (typeof func != "function"){
                            throw new TypeError();
                        }
                        var res = [],
                            thisp = arguments[1];
                        for (var i = 0; i < len; i++) {
                            if (i in t) {
                                var val = t[i];
                                if (func.call(thisp, val, i, t)) {
                                    res.push(val);
                                }
                            }
                        }
                        return res;
                    };
                }

                var defaults = {
                        textarea: null,
                        replaceTab: true,
                        softTabs: true,
                        tabSize: 4,
                        autoOpen: true,
                        overwrite: true,
                        autoStrip: true,
                        autoIndent: true,
                        fence: false
                    },
                    tab,
                    newLine,
                    charSettings = {

                        keyMap: [
                            { open: "\"", close: "\"", canBreak: false },
                            { open: "'", close: "'", canBreak: false },
                            { open: "(", close: ")", canBreak: false },
                            { open: "[", close: "]", canBreak: true },
                            { open: "{", close: "}", canBreak: true }
                        ]

                    },
                    utils = {

                        _callHook: function(hookName, passData){
                            var hooks = BehaveHooks.get(hookName);
                            passData = typeof passData=="boolean" && passData === false ? false : true;

                            if(hooks){
                                if(passData){
                                    var theEditor = defaults.textarea,
                                        textVal = theEditor.value,
                                        caretPos = utils.cursor.get(),
                                        i;

                                    for(i=0; i<hooks.length; i++){
                                        hooks[i].call(undefined, {
                                            editor: {
                                                element: theEditor,
                                                text: textVal,
                                                levelsDeep: utils.levelsDeep()
                                            },
                                            caret: {
                                                pos: caretPos
                                            },
                                            lines: {
                                                current: utils.cursor.getLine(textVal, caretPos),
                                                total: utils.editor.getLines(textVal)
                                            }
                                        });
                                    }
                                } else {
                                    for(i=0; i<hooks.length; i++){
                                        hooks[i].call(undefined);
                                    }
                                }
                            }
                        },

                        defineNewLine: function(){
                            var ta = document.createElement('textarea');
                            ta.value = "\n";

                            if(ta.value.length==2){
                                newLine = "\r\n";
                            } else {
                                newLine = "\n";
                            }
                        },
                        defineTabSize: function(tabSize){
                            if(typeof defaults.textarea.style.OTabSize != "undefined"){
                                defaults.textarea.style.OTabSize = tabSize; return;
                            }
                            if(typeof defaults.textarea.style.MozTabSize != "undefined"){
                                defaults.textarea.style.MozTabSize = tabSize; return;
                            }
                            if(typeof defaults.textarea.style.tabSize != "undefined"){
                                defaults.textarea.style.tabSize = tabSize; return;
                            }
                        },
                        cursor: {
                            getLine: function(textVal, pos){
                                return ((textVal.substring(0,pos)).split("\n")).length;
                            },
                            get: function() {

                                if (typeof document.createElement('textarea').selectionStart==="number") {
                                    return defaults.textarea.selectionStart;
                                } else if (document.selection) {
                                    var caretPos = 0,
                                        range = defaults.textarea.createTextRange(),
                                        rangeDupe = document.selection.createRange().duplicate(),
                                        rangeDupeBookmark = rangeDupe.getBookmark();
                                    range.moveToBookmark(rangeDupeBookmark);

                                    while (range.moveStart('character' , -1) !== 0) {
                                        caretPos++;
                                    }
                                    return caretPos;
                                }
                            },
                            set: function (start, end) {
                                if(!end){
                                    end = start;
                                }
                                if (defaults.textarea.setSelectionRange) {
                                    defaults.textarea.focus();
                                    defaults.textarea.setSelectionRange(start, end);
                                } else if (defaults.textarea.createTextRange) {
                                    var range = defaults.textarea.createTextRange();
                                    range.collapse(true);
                                    range.moveEnd('character', end);
                                    range.moveStart('character', start);
                                    range.select();
                                }
                            },
                            selection: function(){
                                var textAreaElement = defaults.textarea,
                                    start = 0,
                                    end = 0,
                                    normalizedValue,
                                    range,
                                    textInputRange,
                                    len,
                                    endRange;

                                if (typeof textAreaElement.selectionStart == "number" && typeof textAreaElement.selectionEnd == "number") {
                                    start = textAreaElement.selectionStart;
                                    end = textAreaElement.selectionEnd;
                                } else {
                                    range = document.selection.createRange();

                                    if (range && range.parentElement() == textAreaElement) {

                                        normalizedValue = utils.editor.get();
                                        len = normalizedValue.length;

                                        textInputRange = textAreaElement.createTextRange();
                                        textInputRange.moveToBookmark(range.getBookmark());

                                        endRange = textAreaElement.createTextRange();
                                        endRange.collapse(false);

                                        if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
                                            start = end = len;
                                        } else {
                                            start = -textInputRange.moveStart("character", -len);
                                            start += normalizedValue.slice(0, start).split(newLine).length - 1;

                                            if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
                                                end = len;
                                            } else {
                                                end = -textInputRange.moveEnd("character", -len);
                                                end += normalizedValue.slice(0, end).split(newLine).length - 1;
                                            }
                                        }
                                    }
                                }

                                return start==end ? false : {
                                    start: start,
                                    end: end
                                };
                            }
                        },
                        editor: {
                            getLines: function(textVal){
                                return (textVal).split("\n").length;
                            },
                            get: function(){
                                return defaults.textarea.value.replace(/\r/g,'');
                            },
                            set: function(data){
                                defaults.textarea.value = data;
                            }
                        },
                        fenceRange: function(){
                            if(typeof defaults.fence == "string"){

                                var data = utils.editor.get(),
                                    pos = utils.cursor.get(),
                                    hacked = 0,
                                    matchedFence = data.indexOf(defaults.fence),
                                    matchCase = 0;

                                while(matchedFence>=0){
                                    matchCase++;
                                    if( pos < (matchedFence+hacked) ){
                                        break;
                                    }

                                    hacked += matchedFence+defaults.fence.length;
                                    data = data.substring(matchedFence+defaults.fence.length);
                                    matchedFence = data.indexOf(defaults.fence);

                                }

                                if( (hacked) < pos && ( (matchedFence+hacked) > pos ) && matchCase%2===0){
                                    return true;
                                }
                                return false;
                            } else {
                                return true;
                            }
                        },
                        isEven: function(_this,i){
                            return i%2;
                        },
                        levelsDeep: function(){
                            var pos = utils.cursor.get(),
                                val = utils.editor.get();

                            var left = val.substring(0, pos),
                                levels = 0,
                                i, j;

                            for(i=0; i<left.length; i++){
                                for (j=0; j<charSettings.keyMap.length; j++) {
                                    if(charSettings.keyMap[j].canBreak){
                                        if(charSettings.keyMap[j].open == left.charAt(i)){
                                            levels++;
                                        }

                                        if(charSettings.keyMap[j].close == left.charAt(i)){
                                            levels--;
                                        }
                                    }
                                }
                            }

                            var toDecrement = 0,
                                quoteMap = ["'", "\""];
                            for(i=0; i<charSettings.keyMap.length; i++) {
                                if(charSettings.keyMap[i].canBreak){
                                    for(j in quoteMap){
                                        toDecrement += left.split(quoteMap[j]).filter(utils.isEven).join('').split(charSettings.keyMap[i].open).length - 1;
                                    }
                                }
                            }

                            var finalLevels = levels - toDecrement;

                            return finalLevels >=0 ? finalLevels : 0;
                        },
                        deepExtend: function(destination, source) {
                            for (var property in source) {
                                if (source[property] && source[property].constructor &&
                                    source[property].constructor === Object) {
                                    destination[property] = destination[property] || {};
                                    utils.deepExtend(destination[property], source[property]);
                                } else {
                                    destination[property] = source[property];
                                }
                            }
                            return destination;
                        },
                        addEvent: function addEvent(element, eventName, func) {
                            if (element.addEventListener){
                                element.addEventListener(eventName,func,false);
                            } else if (element.attachEvent) {
                                element.attachEvent("on"+eventName, func);
                            }
                        },
                        removeEvent: function addEvent(element, eventName, func){
                            if (element.addEventListener){
                                element.removeEventListener(eventName,func,false);
                            } else if (element.attachEvent) {
                                element.detachEvent("on"+eventName, func);
                            }
                        },

                        preventDefaultEvent: function(e){
                            if(e.preventDefault){
                                e.preventDefault();
                            } else {
                                e.returnValue = false;
                            }
                        }
                    },
                    intercept = {
                        tabKey: function (e) {

                            if(!utils.fenceRange()){ return; }

                            if (e.keyCode == 9) {
                                utils.preventDefaultEvent(e);

                                var toReturn = true;
                                utils._callHook('tab:before');

                                var selection = utils.cursor.selection(),
                                    pos = utils.cursor.get(),
                                    val = utils.editor.get();

                                if(selection){

                                    var tempStart = selection.start;
                                    while(tempStart--){
                                        if(val.charAt(tempStart)=="\n"){
                                            selection.start = tempStart + 1;
                                            break;
                                        }
                                    }

                                    var toIndent = val.substring(selection.start, selection.end),
                                        lines = toIndent.split("\n"),
                                        i;

                                    if(e.shiftKey){
                                        for(i = 0; i<lines.length; i++){
                                            if(lines[i].substring(0,tab.length) == tab){
                                                lines[i] = lines[i].substring(tab.length);
                                            }
                                        }
                                        toIndent = lines.join("\n");

                                        utils.editor.set( val.substring(0,selection.start) + toIndent + val.substring(selection.end) );
                                        utils.cursor.set(selection.start, selection.start+toIndent.length);

                                    } else {
                                        for(i in lines){
                                            lines[i] = tab + lines[i];
                                        }
                                        toIndent = lines.join("\n");

                                        utils.editor.set( val.substring(0,selection.start) + toIndent + val.substring(selection.end) );
                                        utils.cursor.set(selection.start, selection.start+toIndent.length);
                                    }
                                } else {
                                    var left = val.substring(0, pos),
                                        right = val.substring(pos),
                                        edited = left + tab + right;

                                    if(e.shiftKey){
                                        if(val.substring(pos-tab.length, pos) == tab){
                                            edited = val.substring(0, pos-tab.length) + right;
                                            utils.editor.set(edited);
                                            utils.cursor.set(pos-tab.length);
                                        }
                                    } else {
                                        utils.editor.set(edited);
                                        utils.cursor.set(pos + tab.length);
                                        toReturn = false;
                                    }
                                }
                                utils._callHook('tab:after');
                            }
                            return toReturn;
                        },
                        enterKey: function (e) {

                            if(!utils.fenceRange()){ return; }

                            if (e.keyCode == 13) {

                                utils.preventDefaultEvent(e);
                                utils._callHook('enter:before');

                                var pos = utils.cursor.get(),
                                    val = utils.editor.get(),
                                    left = val.substring(0, pos),
                                    right = val.substring(pos),
                                    leftChar = left.charAt(left.length - 1),
                                    rightChar = right.charAt(0),
                                    numTabs = utils.levelsDeep(),
                                    ourIndent = "",
                                    closingBreak = "",
                                    finalCursorPos,
                                    i;
                                if(!numTabs){
                                    finalCursorPos = 1;
                                } else {
                                    while(numTabs--){
                                        ourIndent+=tab;
                                    }
                                    ourIndent = ourIndent;
                                    finalCursorPos = ourIndent.length + 1;

                                    for(i=0; i<charSettings.keyMap.length; i++) {
                                        if (charSettings.keyMap[i].open == leftChar && charSettings.keyMap[i].close == rightChar){
                                            closingBreak = newLine;
                                        }
                                    }

                                }

                                var edited = left + newLine + ourIndent + closingBreak + (ourIndent.substring(0, ourIndent.length-tab.length) ) + right;
                                utils.editor.set(edited);
                                utils.cursor.set(pos + finalCursorPos);
                                utils._callHook('enter:after');
                            }
                        },
                        deleteKey: function (e) {

                            if(!utils.fenceRange()){ return; }

                            if(e.keyCode == 8){
                                utils.preventDefaultEvent(e);

                                utils._callHook('delete:before');

                                var pos = utils.cursor.get(),
                                    val = utils.editor.get(),
                                    left = val.substring(0, pos),
                                    right = val.substring(pos),
                                    leftChar = left.charAt(left.length - 1),
                                    rightChar = right.charAt(0),
                                    i;

                                if( utils.cursor.selection() === false ){
                                    for(i=0; i<charSettings.keyMap.length; i++) {
                                        if (charSettings.keyMap[i].open == leftChar && charSettings.keyMap[i].close == rightChar) {
                                            var edited = val.substring(0,pos-1) + val.substring(pos+1);
                                            utils.editor.set(edited);
                                            utils.cursor.set(pos - 1);
                                            return;
                                        }
                                    }
                                    var edited = val.substring(0,pos-1) + val.substring(pos);
                                    utils.editor.set(edited);
                                    utils.cursor.set(pos - 1);
                                } else {
                                    var sel = utils.cursor.selection(),
                                        edited = val.substring(0,sel.start) + val.substring(sel.end);
                                    utils.editor.set(edited);
                                    utils.cursor.set(pos);
                                }

                                utils._callHook('delete:after');

                            }
                        }
                    },
                    charFuncs = {
                        openedChar: function (_char, e) {
                            utils.preventDefaultEvent(e);
                            utils._callHook('openChar:before');
                            var pos = utils.cursor.get(),
                                val = utils.editor.get(),
                                left = val.substring(0, pos),
                                right = val.substring(pos),
                                edited = left + _char.open + _char.close + right;

                            defaults.textarea.value = edited;
                            utils.cursor.set(pos + 1);
                            utils._callHook('openChar:after');
                        },
                        closedChar: function (_char, e) {
                            var pos = utils.cursor.get(),
                                val = utils.editor.get(),
                                toOverwrite = val.substring(pos, pos + 1);
                            if (toOverwrite == _char.close) {
                                utils.preventDefaultEvent(e);
                                utils._callHook('closeChar:before');
                                utils.cursor.set(utils.cursor.get() + 1);
                                utils._callHook('closeChar:after');
                                return true;
                            }
                            return false;
                        }
                    },
                    action = {
                        filter: function (e) {

                            if(!utils.fenceRange()){ return; }

                            var theCode = e.which || e.keyCode;

                            if(theCode == 39 || theCode == 40 && e.which===0){ return; }

                            var _char = String.fromCharCode(theCode),
                                i;

                            for(i=0; i<charSettings.keyMap.length; i++) {

                                if (charSettings.keyMap[i].close == _char) {
                                    var didClose = defaults.overwrite && charFuncs.closedChar(charSettings.keyMap[i], e);

                                    if (!didClose && charSettings.keyMap[i].open == _char && defaults.autoOpen) {
                                        charFuncs.openedChar(charSettings.keyMap[i], e);
                                    }
                                } else if (charSettings.keyMap[i].open == _char && defaults.autoOpen) {
                                    charFuncs.openedChar(charSettings.keyMap[i], e);
                                }
                            }
                        },
                        listen: function () {

                            if(defaults.replaceTab){ utils.addEvent(defaults.textarea, 'keydown', intercept.tabKey); }
                            if(defaults.autoIndent){ utils.addEvent(defaults.textarea, 'keydown', intercept.enterKey); }
                            if(defaults.autoStrip){ utils.addEvent(defaults.textarea, 'keydown', intercept.deleteKey); }

                            utils.addEvent(defaults.textarea, 'keypress', action.filter);

                            utils.addEvent(defaults.textarea, 'keydown', function(){ utils._callHook('keydown'); });
                            utils.addEvent(defaults.textarea, 'keyup', function(){ utils._callHook('keyup'); });
                        }
                    },
                    init = function (opts) {

                        if(opts.textarea){
                            utils._callHook('init:before', false);
                            utils.deepExtend(defaults, opts);
                            utils.defineNewLine();

                            if (defaults.softTabs) {
                                tab = " ".repeat(defaults.tabSize);
                            } else {
                                tab = "\t";

                                utils.defineTabSize(defaults.tabSize);
                            }

                            action.listen();
                            utils._callHook('init:after', false);
                        }

                    };

                this.destroy = function(){
                    utils.removeEvent(defaults.textarea, 'keydown', intercept.tabKey);
                    utils.removeEvent(defaults.textarea, 'keydown', intercept.enterKey);
                    utils.removeEvent(defaults.textarea, 'keydown', intercept.deleteKey);
                    utils.removeEvent(defaults.textarea, 'keypress', action.filter);
                };

                init(userOpts);

            };

    if (typeof module !== 'undefined' && module.exports) {
        module.exports = Behave;
    }

    if (typeof ender === 'undefined') {
        this.Behave = Behave;
        this.BehaveHooks = BehaveHooks;
    }

    if (typeof define === "function" && define.amd) {
        define("behave", [], function () {
            return Behave;
        });
    }

}).call(this);


( function( $ ) {

    $(document).on( 'sowsetupformfield', '.siteorigin-widget-field-type-code', function(e) {
        var $$ = $(this);
        var editor = new Behave({
            textarea: $$.find( '.siteorigin-widget-code-input' ).get( 0 ),
        });
    } );

} )( jQuery );